/* 
 * Copyright (C) 2008 Torgny Bjers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sugree.jibjib;

import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;

import java.util.Locale;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentUris;
import android.content.Intent;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.ClipboardManager;
import android.text.Html;
import android.text.util.Linkify;
import android.util.Log;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebChromeClient;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.view.Window;

import com.sugree.jibjib.data.FriendTimeline;
import com.sugree.jibjib.data.JibJibDatabase;
import com.sugree.jibjib.data.JibJibDatabase.Tweets;
import com.sugree.jibjib.data.Tweet;
import com.sugree.jibjib.net.Connection;
import com.sugree.jibjib.net.ConnectionAuthenticationException;
import com.sugree.jibjib.net.ConnectionException;
import com.sugree.jibjib.net.ConnectionUnavailableException;
import com.sugree.jibjib.util.RelativeTime;

/**
 * @author torgny.bjers
 *
 */
public class TweetActivity extends Activity {

	protected static final String TAG = "JibJib";

	protected final String[] PROJECTION = new String[] {
		Tweets._ID,
		Tweets.AUTHOR_ID,
		Tweets.MESSAGE,
		Tweets.SOURCE,
		Tweets.FAVORITED,
		Tweets.IN_REPLY_TO_AUTHOR_ID,
		Tweets.IN_REPLY_TO_STATUS_ID,
		Tweets.SENT_DATE,
		Tweets.TWEET_TYPE,
		Tweets.LATITUDE,
		Tweets.LONGITUDE
	};

	public static final int DIALOG_AUTHENTICATION_FAILED = 1;
	public static final int DIALOG_SERVICE_UNAVAILABLE = 2;
	public static final int DIALOG_CONNECTION_TIMEOUT = 3;
	public static final int DIALOG_EXECUTING_COMMAND = 4;

	protected static final String GEO_PATTERN = "\\b(geo|l):([0-9.]+),([0-9.]+)\\b";
	protected static final String GEO_SCHEME = "http://maps.google.com/maps?q=";

	protected static final String ME_PATTERN = "\\bhttps?://[a-zA-Z0-9.]+\\.me/\\S+\\b";

	protected static final String MENTION_PATTERN = "@([a-zA-Z0-9_]+)";
	protected static final String MENTION_SCHEME = "jibjib://users/profile/";

	protected static final String HASHTAG_PATTERN = "#([a-zA-Z0-9_]+)";
	protected static final String HASHTAG_SCHEME = "content://com.sugree.jibjib/tweets/search/";

	protected static final String TWITPIC_PATTERN = "https?://(www\\.)?(twitpic\\.com)/([a-zA-Z0-9]+)";
	protected static final String TWITGOO_PATTERN = "https?://(www\\.)?(twitgoo\\.com)/([a-zA-Z0-9]+)";
	protected static final String IMGLY_PATTERN = "https?://(www\\.)?(img\\.ly)/([a-zA-Z0-9]+)";
	protected static final String YFROG_PATTERN = "https?://(www\\.)?(yfrog\\.com)/([a-zA-Z0-9]+)";
	protected static final String FLICKR_PATTERN = "https?://(www\\.)?(flic\\.kr)/p/([a-zA-Z0-9]+)";
	protected static final String TWEETPHOTO_PATTERN = "https?://(www\\.)?(tweetphoto\\.com)/([0-9]+)";
	protected static final String PLIXI_PATTERN = "https?://(www\\.)?(plixi\\.com)/p/([0-9]+)";
	protected static final String PICGD_PATTERN = "https?://(www\\.)?(pic\\.gd)/([a-zA-Z0-9]+)";
	protected static final String UPICME1_PATTERN = "https?://(www\\.)?(upic\\.me)/([a-zA-Z0-9]{3,})";
	protected static final String UPICME2_PATTERN = "https?://(www\\.)?(upic\\.me)/i/([a-zA-Z0-9]{2}/[a-zA-Z0-9_-]+\\.[a-zA-Z0-9]+)";
	protected static final String MOBYPICTURE_PATTERN = "https?://(www\\.)?(moby\\.to)/([a-zA-Z0-9]+)";
	protected static final String PICPLZ_PATTERN = "https?://(www\\.)?(picplz\\.com)/([a-zA-Z0-9]+)";

	protected Uri mUri;
	protected Cursor mCursor;

	protected TextView mAuthor;
	protected TextView mMessage;
	protected TextView mSentDate;
	protected Button mReply;
	protected Button mReplyAll;
	protected Button mReplyQuote;
	protected Button mReplyTo;
	protected Button mRetweet;
	protected Button mDirectMessage;
	protected Button mFavorite;
	protected Button mDestroyFavorite;
	protected Button mCopy;
	protected Button mShare;
	protected Button mBack;
	protected WebView mWebView;
	protected ProgressBar mProgressBar;

	protected Exception mException = null;
	protected ProgressDialog mProgressDialog;
	protected SharedPreferences mSP;
	protected long mReplyId;
	protected Tweet mTweet = null;
	protected boolean mFatal = false;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
		mSP = PreferenceManager.getDefaultSharedPreferences(this);

		loadTheme();

		setContentView(R.layout.tweetview);

		final Intent intent = getIntent();
		mUri = intent.getData();

		mAuthor = (TextView) findViewById(R.id.tweet_screen_name);
		mMessage = (TextView) findViewById(R.id.tweet_message);
		mSentDate = (TextView) findViewById(R.id.tweet_sent);
		mWebView = (WebView) findViewById(R.id.webview);
		mProgressBar = (ProgressBar) findViewById(R.id.progressbar);

		mReply = (Button) findViewById(R.id.button_reply);
		mReplyAll = (Button) findViewById(R.id.button_reply_all);
		mReplyQuote = (Button) findViewById(R.id.button_reply_quote);
		mReplyTo = (Button) findViewById(R.id.button_reply_to);
		mRetweet = (Button) findViewById(R.id.button_retweet);
		mDirectMessage = (Button) findViewById(R.id.button_direct_message);
		mFavorite = (Button) findViewById(R.id.button_favorite);
		mDestroyFavorite = (Button) findViewById(R.id.button_destroy_favorite);
		mCopy = (Button) findViewById(R.id.button_copy);
		mShare = (Button) findViewById(R.id.button_share);
		mBack = (Button) findViewById(R.id.button_back);

		mCursor = managedQuery(mUri, null, null, null, null);

		mReply.setOnClickListener(mButtonListener);
		mReplyAll.setOnClickListener(mButtonListener);
		mReplyQuote.setOnClickListener(mButtonListener);
		mReplyTo.setOnClickListener(mButtonListener);
		mRetweet.setOnClickListener(mButtonListener);
		mDirectMessage.setOnClickListener(mButtonListener);
		mFavorite.setOnClickListener(mButtonListener);
		mDestroyFavorite.setOnClickListener(mButtonListener);
		mCopy.setOnClickListener(mButtonListener);
		mShare.setOnClickListener(mButtonListener);
		mBack.setOnClickListener(mButtonListener);

		mTweet = savedInstanceState != null
				? (Tweet)savedInstanceState.getParcelable("tweet")
				: null;
	}

	@Override
	protected void onResume() {
		super.onResume();
		long id = 0;
		if (mTweet != null) {
			try {
				id = ContentUris.parseId(mUri);
			} catch (NumberFormatException e) {
				id = 0;
			}
		}
		if (mTweet == null || mTweet.getId() != id) {
			handleResume();
		} else {
			handleTweet(mTweet);
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle state) {
		super.onSaveInstanceState(state);
		state.putParcelable("tweet", mTweet);
	}

	protected void handleTweet(Tweet t) {
		mTweet = t;

		mAuthor.setText(mTweet.getScreenName());
		mMessage.setLinksClickable(true);
		mMessage.setFocusable(true);
		mMessage.setFocusableInTouchMode(true);
		mMessage.setText(mTweet.getText());
		Linkify.addLinks(mMessage, Linkify.ALL);

		Pattern geoMatcher = Pattern.compile(GEO_PATTERN);
		Linkify.addLinks(mMessage, geoMatcher, GEO_SCHEME, new GeoMatchFilter(), new GeoTransform());

		Pattern meMatcher = Pattern.compile(ME_PATTERN);
		Linkify.addLinks(mMessage, meMatcher, "");

		Pattern mentionMatcher = Pattern.compile(MENTION_PATTERN);
		Linkify.addLinks(mMessage, mentionMatcher, MENTION_SCHEME, new MentionMatchFilter(), new MentionTransform());

		Pattern hashtagMatcher = Pattern.compile(HASHTAG_PATTERN);
		Linkify.addLinks(mMessage, hashtagMatcher, HASHTAG_SCHEME, new HashTagMatchFilter(), new HashTagTransform());

		String inReplyTo = "";
		mReplyId = mTweet.getToStatusId();
		if (mReplyId > 0) {
			inReplyTo = String.format(
					Locale.getDefault(),
					getText(R.string.tweet_source_in_reply_to).toString(),
					mTweet.getToScreenName());
			mReplyTo.setVisibility(View.VISIBLE);
		}

		String sentDate = String.format(
			Locale.getDefault(), 
			getText(R.string.tweet_source_from).toString(), 
			RelativeTime.getDifference(this, mTweet.getSentDate()), 
			Html.fromHtml(mTweet.getSource()),
			inReplyTo
		);
		mSentDate.setText(sentDate);

		if (mTweet.getFavorited()) {
			mDestroyFavorite.setVisibility(View.VISIBLE);
			mFavorite.setVisibility(View.GONE);
		}

		extractUrl(mTweet.getText());
	}

	protected void handleResume() {
		if (mCursor != null && mCursor.getCount() > 0) {
			mCursor.moveToFirst();
			Tweet t = Tweet.fetchOneTweet(mSP.getString("twitter_username", ""), mCursor);
			handleTweet(t);
		} else {
			Log.e(TAG, "No cursor found");
			new FetchTask().execute(mUri);
		}
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_AUTHENTICATION_FAILED:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_authentication_failed)
				.setMessage(R.string.dialog_summary_authentication_failed)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						finish();
						startActivity(new Intent(TweetActivity.this, PreferencesActivity.class));
					}
				}).create();

		case DIALOG_SERVICE_UNAVAILABLE:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_service_unavailable)
				.setMessage(getString(R.string.dialog_summary_service_unavailable, new Object[] {mException.getMessage()}))
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						if (mFatal) {
							finish();
						}
					}
				}).create();

		case DIALOG_CONNECTION_TIMEOUT:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_connection_timeout)
				.setMessage(R.string.dialog_summary_connection_timeout)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						if (mFatal) {
							finish();
						}
					}
				}).create();

		case DIALOG_EXECUTING_COMMAND:
			mProgressDialog = new ProgressDialog(this);
			mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
			mProgressDialog.setTitle(R.string.dialog_title_executing_command);
			mProgressDialog.setMessage(getText(R.string.dialog_summary_executing_command));
			return mProgressDialog;

		default:
			return super.onCreateDialog(id);
		}
	}

	protected String[] getProjection() {
		return PROJECTION;
	}

	/**
	 * Load the theme for preferences.
	 */
	protected void loadTheme() {
		boolean light = mSP.getBoolean("appearance_light_theme", false);
		StringBuilder theme = new StringBuilder();
		String name = mSP.getString("theme", "JibJib");
		if (name.indexOf("Theme.") > -1) {
			name = name.substring(name.indexOf("Theme."));
		}
		theme.append("Theme.");
		if (light) {
			theme.append("Light.");
		}
		theme.append(name);
		setTheme((int) getResources().getIdentifier(theme.toString(), "style", "com.sugree.jibjib"));
	}

	@Override
	public boolean onSearchRequested() {
		Bundle appDataBundle = new Bundle();
		appDataBundle.putParcelable("content_uri", JibJibDatabase.Tweets.SEARCH_URI);
		startSearch(null, false, appDataBundle, false);
		return true;
	}

	protected void extractUrl(String message) {
		StringBuffer content = new StringBuffer();
		String url = null;
		Pattern p;
		Matcher m;

		p = Pattern.compile(TWITPIC_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://twitpic.com/show/thumb/"+id+"\">");
		}

		p = Pattern.compile(TWITGOO_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://twitgoo.com/show/thumb/"+id+"\">");
		}

		p = Pattern.compile(IMGLY_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://img.ly/show/medium/"+id+"\">");
		}

		p = Pattern.compile(YFROG_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://yfrog.com/"+id+".th.jpg\">");
		}

		p = Pattern.compile(FLICKR_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://flic.kr/p/img/"+id+"_m.jpg\">");
		}

		p = Pattern.compile(TWEETPHOTO_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://api.plixi.com/api/tpapi.svc/imagefromurl?size=medium&url=http://tweetphoto.com/"+id+"\">");
		}

		p = Pattern.compile(PLIXI_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://api.plixi.com/api/tpapi.svc/imagefromurl?size=medium&url=http://plixi.com/p/"+id+"\">");
		}

		p = Pattern.compile(PICGD_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://TweetPhotoAPI.com/api/TPAPI.svc/imagefromurl?size=medium&url=http://pic.gd/"+id+"\">");
		}

		p = Pattern.compile(UPICME1_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://upic.me/show/thumb/"+id+"\">");
		}

		p = Pattern.compile(UPICME2_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://upic.me/tw/i/"+id+"\">");
		}

		p = Pattern.compile(PICPLZ_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			url = m.group(0);
		}

		p = Pattern.compile(MOBYPICTURE_PATTERN);
		m = p.matcher(message);
		while (m.find()) {
			String service = m.group(2);
			String id = m.group(3);
			content.append("<img src=\"http://api.mobypicture.com/?t=http://moby.to/"+id+"&s=small&k="+PrivateData.MOBYPICTURE_API_KEY+"&format=plain\">");
		}

		if (mTweet != null && mTweet.getLocation() != null) {
			String location = mTweet.getLocation();
			String size = "290x200";
			content.append("<img src=\"http://maps.google.com/maps/api/staticmap?center="+location+"&zoom=14&size="+size+"&maptype=roadmap&markers=color:red|"+location+"&mobile=true&sensor=true\">");
		}

		String text = content.toString();
		//Log.d(TAG, "text "+text);
		if (text.length() > 0 || url != null) {
			mWebView.setWebChromeClient(new WebChromeClient() {
				public void onProgressChanged(WebView view, int progress) {
					mProgressBar.setProgress(progress);
				}
			});
			mWebView.setWebViewClient(new WebViewClient() {
				public void onPageFinished(WebView view, String url) {
					mProgressBar.setVisibility(View.GONE);
				}

				public void onPageStarted(WebView view, String url) {
					mProgressBar.setVisibility(View.VISIBLE);
				}
			});
			mProgressBar.setVisibility(View.VISIBLE);
			mWebView.setVisibility(View.VISIBLE);
			if (url != null) {
				mWebView.loadUrl(url);
			} else {
				mWebView.loadData(text, "text/html", "utf-8");
			}
		}
	}

	protected void sendTweet(String text) {
		final Intent intent = new Intent(TweetActivity.this, SendTweet.class);
		Bundle appDataBundle = new Bundle();
		appDataBundle.putLong("reply_id", mTweet.getId());

		intent.putExtra("com.sugree.jibjib.app_data", appDataBundle);
		intent.putExtra(Intent.EXTRA_SUBJECT, text);
		intent.setAction(Intent.ACTION_SEND);
		startActivity(intent);
	}

	protected void retweet() {
		String mode = mSP.getString("retweet_mode", "choose");

		if (mode.equals("auto")) {
			String text = mTweet.retweet();
			if (text.length() > 140) {
				mode = "non-modifiable";
			} else {
				mode = "modifiable";
			}
		}
		if (mTweet.getProtected()) {
			mode = "modifiable";
		}

		if (mode.equals("choose")) {
			String[] choices = new String[2];
			choices[0] = getString(R.string.retweet_old);
			choices[1] = getString(R.string.retweet_new);

			AlertDialog dialog = new AlertDialog.Builder(this)
				.setTitle(R.string.retweet_mode)
				.setCancelable(true)
				.setItems(choices, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						switch (id) {
						case 0:
							sendTweet(mTweet.retweet());
							break;

						case 1:
							new RetweetTask().execute(mTweet);
							break;
						}
					}
				})
				.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						dialog.cancel();
					}
				}).create();
			dialog.show();
		} else if (mode.equals("modifiable")) {
			sendTweet(mTweet.retweet());
		} else if (mode.equals("non-modifiable")) {
			new RetweetTask().execute(mTweet);
		}
	}

	protected void copyTweet(String text) {
		ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
		clipboard.setText(text);
		Toast.makeText(TweetActivity.this, R.string.copied, Toast.LENGTH_SHORT).show();
	}

	protected void shareTweet(String text) {
		Intent intent = new Intent(Intent.ACTION_SEND);
		intent.setType("text/plain");
		intent.putExtra(Intent.EXTRA_TEXT, text);
		startActivity(Intent.createChooser(intent, getText(R.string.get_share)));
	}

	protected View.OnClickListener mButtonListener = new View.OnClickListener() {
		public void onClick(View v) {
			if (v == mReply) {
				sendTweet(mTweet.reply());
			} else if (v == mReplyAll) {
				sendTweet(mTweet.replyAll());
			} else if (v == mReplyQuote) {
				sendTweet(mTweet.replyQuote());
			} else if (v == mReplyTo) {
				Uri uri = ContentUris.withAppendedId(Tweets.CONTENT_URI, mReplyId);
				startActivity(new Intent(Intent.ACTION_VIEW, uri));
			} else if (v == mRetweet) {
				retweet();
			} else if (v == mDirectMessage) {
				sendTweet(mTweet.replyDirectMessage());
			} else if (v == mFavorite) {
				new FavoriteTask().execute(mTweet);
			} else if (v == mDestroyFavorite) {
				new FavoriteTask().execute(mTweet);
			} else if (v == mCopy) {
				copyTweet(mTweet.toString());
			} else if (v == mShare) {
				shareTweet(mTweet.toString());
			} else if (v == mBack) {
				finish();
			}
		}
	};

	public class GeoTransform implements Linkify.TransformFilter {
		public String transformUrl(Matcher match, String url) {
			String geo = match.group(2)+","+match.group(3);
			return geo;
		}
	}

	public class GeoMatchFilter implements Linkify.MatchFilter {
		public boolean acceptMatch(CharSequence s, int start, int end) {
			return true;
		}
	}

	public class MentionTransform implements Linkify.TransformFilter {
		public String transformUrl(Matcher match, String url) {
			return match.group(1);
		}
	}

	public class MentionMatchFilter implements Linkify.MatchFilter {
		public boolean acceptMatch(CharSequence s, int start, int end) {
			return true;
		}
	}

	public class HashTagTransform implements Linkify.TransformFilter {
		public String transformUrl(Matcher match, String url) {
			return Uri.encode(match.group(0));
		}
	}

	public class HashTagMatchFilter implements Linkify.MatchFilter {
		public boolean acceptMatch(CharSequence s, int start, int end) {
			return true;
		}
	}

	public class FavoriteTask extends AsyncTask<Tweet, Integer, JSONObject> {
		protected boolean mFavorited;

		protected JSONObject doInBackground(Tweet... tweets) {
			/*
			Connection con = new Connection(
					mSP.getString("twitter_username", null),
					mSP.getString("twitter_password", null));
			con.setTwitterGateway(mSP.getString("twitter_gateway", null));
			*/
			Connection con = new Connection(mSP);
			JSONObject result = new JSONObject();
			try {
				publishProgress(0);
				long id = mCursor.getLong(mCursor.getColumnIndex(Tweets._ID));
				int tweetType = mCursor.getInt(mCursor.getColumnIndex(Tweets.TWEET_TYPE));
				mFavorited = mCursor.getInt(mCursor.getColumnIndex(Tweets.FAVORITED)) == 1;
				if (mFavorited) {
					result = con.destroyFavorite(id);
				} else {
					result = con.createFavorite(id);
				}
				FriendTimeline ft = new FriendTimeline(getContentResolver(), null, null, 0);
				mFavorited = !mFavorited;
				result.put("favorited", mFavorited);
				ft.insertFromJSONObject(result, tweetType);
				/*
				mFavorited = result.getBoolean("favorited");
				ft.setFavorite(id, mFavorited);
				*/
				mCursor.requery();
				mCursor.moveToFirst();
				publishProgress(100);
			} catch (JSONException e) {
				Log.e(TAG, e.getMessage());
				mException = e;
			} catch (UnsupportedEncodingException e) {
				Log.e(TAG, e.getMessage());
				mException = e;
			} catch (ConnectionException e) {
				Log.e(TAG, "FavoriteTask "+e.getMessage());
				mException = e;
			} catch (ConnectionAuthenticationException e) {
				Log.e(TAG, "FavoriteTask "+e.getMessage());
				mException = e;
			} catch (ConnectionUnavailableException e) {
				Log.e(TAG, "FavoriteTask "+e.getMessage());
				mException = e;
			} catch (SocketTimeoutException e) {
				Log.e(TAG, "FavoriteTask "+e.getMessage());
				mException = e;
			}
			return result;
		}

		protected void onProgressUpdate(Integer... progress) {
			Log.d(TAG, "progress "+progress);
			showDialog(DIALOG_EXECUTING_COMMAND);
		}

		protected void onPostExecute(JSONObject result) {
			dismissDialog(DIALOG_EXECUTING_COMMAND);
			if (mException == null) {
				if (mFavorited) {
					mDestroyFavorite.setVisibility(View.VISIBLE);
					mFavorite.setVisibility(View.GONE);
					Toast.makeText(TweetActivity.this, R.string.favorite_created, Toast.LENGTH_SHORT).show();
				} else {
					mDestroyFavorite.setVisibility(View.GONE);
					mFavorite.setVisibility(View.VISIBLE);
					Toast.makeText(TweetActivity.this, R.string.favorite_destroyed, Toast.LENGTH_SHORT).show();
				}
				return;
			}
			if (mException instanceof ConnectionException) {
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof JSONException) {
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof ConnectionAuthenticationException) {
				showDialog(DIALOG_AUTHENTICATION_FAILED);
			} else if (mException instanceof ConnectionUnavailableException) {
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof SocketTimeoutException) {
				showDialog(DIALOG_CONNECTION_TIMEOUT);
			}
		}
	}

	public class RetweetTask extends AsyncTask<Tweet, Integer, JSONObject> {
		protected boolean mFavorited;

		protected JSONObject doInBackground(Tweet... tweets) {
			/*
			Connection con = new Connection(
					mSP.getString("twitter_username", null),
					mSP.getString("twitter_password", null));
			con.setTwitterGateway(mSP.getString("twitter_gateway", null));
			*/
			Connection con = new Connection(mSP);
			JSONObject result = new JSONObject();
			try {
				publishProgress(0);
				result = con.retweetStatus(tweets[0].getId());
				publishProgress(100);
			} catch (UnsupportedEncodingException e) {
				Log.e(TAG, e.getMessage());
				mException = e;
			} catch (ConnectionException e) {
				Log.e(TAG, "RetweetTask "+e.getMessage());
				mException = e;
			} catch (ConnectionAuthenticationException e) {
				Log.e(TAG, "RetweetTask "+e.getMessage());
				mException = e;
			} catch (ConnectionUnavailableException e) {
				Log.e(TAG, "RetweetTask "+e.getMessage());
				mException = e;
			} catch (SocketTimeoutException e) {
				Log.e(TAG, "RetweetTask "+e.getMessage());
				mException = e;
			}
			return result;
		}

		protected void onProgressUpdate(Integer... progress) {
			Log.d(TAG, "progress "+progress);
			showDialog(DIALOG_EXECUTING_COMMAND);
		}

		protected void onPostExecute(JSONObject result) {
			dismissDialog(DIALOG_EXECUTING_COMMAND);
			if (mException == null) {
				Toast.makeText(TweetActivity.this, R.string.retweeted, Toast.LENGTH_SHORT).show();
				return;
			}
			if (mException instanceof ConnectionException) {
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof JSONException) {
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof ConnectionAuthenticationException) {
				showDialog(DIALOG_AUTHENTICATION_FAILED);
			} else if (mException instanceof ConnectionUnavailableException) {
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof SocketTimeoutException) {
				showDialog(DIALOG_CONNECTION_TIMEOUT);
			}
		}
	}

	public class FetchTask extends AsyncTask<Uri, Integer, Tweet> {
		protected Tweet doInBackground(Uri... uris) {
			/*
			Connection con = new Connection(
					mSP.getString("twitter_username", null),
					mSP.getString("twitter_password", null));
			con.setTwitterGateway(mSP.getString("twitter_gateway", null));
			*/
			Connection con = new Connection(mSP);
			Tweet result = null;
			try {
				publishProgress(0);
				JSONObject obj = con.getStatus(ContentUris.parseId(uris[0]));
				result = Tweet.fetchOneTweet("", obj);
				publishProgress(100);
			} catch (NumberFormatException e) {
				Log.e(TAG, "FetchTask "+e.getMessage());
				mException = e;
			} catch (JSONException e) {
				Log.e(TAG, "FetchTask "+e.getMessage(), e);
				mException = e;
			} catch (ConnectionException e) {
				Log.e(TAG, "FetchTask "+e.getMessage());
				mException = e;
			} catch (ConnectionAuthenticationException e) {
				Log.e(TAG, "FetchTask "+e.getMessage());
				mException = e;
			} catch (ConnectionUnavailableException e) {
				Log.e(TAG, "FetchTask "+e.getMessage());
				mException = e;
			} catch (SocketTimeoutException e) {
				Log.e(TAG, "FetchTask "+e.getMessage());
				mException = e;
			} catch (Exception e) {
				Log.e(TAG, "FetchTask "+e.getMessage());
				mException = e;
			}
			return result;
		}

		protected void onProgressUpdate(Integer... progress) {
			Log.d(TAG, "progress "+progress);
			showDialog(DIALOG_EXECUTING_COMMAND);
		}

		protected void onPostExecute(Tweet result) {
			dismissDialog(DIALOG_EXECUTING_COMMAND);
			if (mException == null) {
				handleTweet(result);
				return;
			}
			if (mException instanceof ConnectionException) {
				mFatal = true;
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof JSONException) {
				mFatal = true;
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof ConnectionAuthenticationException) {
				mFatal = true;
				showDialog(DIALOG_AUTHENTICATION_FAILED);
			} else if (mException instanceof ConnectionUnavailableException) {
				mFatal = true;
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
			} else if (mException instanceof SocketTimeoutException) {
				mFatal = true;
				showDialog(DIALOG_CONNECTION_TIMEOUT);
			}
		}
	}
}
